package com.gdut.huayan.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gdut.huayan.dao.*;
import com.gdut.huayan.entity.dto.PageQuery;
import com.gdut.huayan.entity.po.*;
import com.gdut.huayan.entity.vo.OrderDetailVo;
import com.gdut.huayan.entity.vo.RefundContentVo;
import com.gdut.huayan.entity.vo.RefundDetailVo;
import com.gdut.huayan.entity.vo.RefundVo;
import com.gdut.huayan.service.OrderService;
import com.gdut.huayan.service.RefundService;
import com.gdut.huayan.service.WechatService;
import com.gdut.huayan.service.exception.BaseException;
import com.gdut.huayan.utils.PageInfoUtils;
import com.gdut.huayan.utils.SecurityUtils;
import com.gdut.huayan.utils.StringUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
@Slf4j
public class RefundServiceImpl extends ServiceImpl<RefundMapper, Refund> implements RefundService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private RefundDetailMapper refundDetailMapper;

    @Autowired
    private OrderService orderService;

    @Autowired
    private WechatService wechatService;


    @Override
    public PageInfo<RefundContentVo> getAll(PageQuery pageQuery, Integer status) {
        PageHelper.startPage(pageQuery.getPage(), pageQuery.getSize());
        QueryWrapper<Refund> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotEmpty(pageQuery.getSearch())) {
            queryWrapper.and(wrapper -> wrapper.like("order_number", "%" + pageQuery.getSearch() + "%")
                    .or().like("refund_number", "%" + pageQuery.getSearch() + "%"));
        }
        if ("ASC".equals(pageQuery.getOrder())) {
            queryWrapper.orderByAsc(pageQuery.getOrderKey().split(";"));
        } else if ("DESC".equals(pageQuery.getOrder())) {
            queryWrapper.orderByDesc(pageQuery.getOrderKey().split(";"));
        }
        if (status != null) {
            queryWrapper.eq("status", status);
        }
        PageInfo<Refund> refundPageInfo = new PageInfo<>(baseMapper.selectList(queryWrapper));
        if (refundPageInfo.getList().size() == 0) {
            return null;
        }
        Map<Long, User> userMap = userMapper.selectBatchIds(
                refundPageInfo.getList().stream().map(Refund::getUserId).collect(Collectors.toList()))
                .stream().collect(Collectors.toMap(User::getId, u -> u));
        /*Map<Long,Order> orderMap = orderMapper.selectBatchIds(refundPageInfo.getList().stream().map(Refund::getOrderId).collect(Collectors.toList()))
                .stream().collect(Collectors.toMap(Order::getId, o -> o));*/
        /*for(User user : userMap.values()){
            user.setAvatarFileId(AliyunUtils.getDownloadLink(user.getAvatarUrl(), new Date().getTime() + SecurityConstant.TIME_ONE_DAY));
        }*/
        List<RefundContentVo> refundContentVos = new ArrayList<>();
        refundPageInfo.getList().forEach(r -> {
            refundContentVos.add(new RefundContentVo(r, userMap.get(r.getUserId())));
        });
        return new PageInfoUtils<Refund, RefundContentVo>().copy(refundPageInfo, refundContentVos);
    }

    @Override
    public List<RefundDetailVo> getRefundDetail(Long refundId, Long orderId) {
        List<RefundDetailVo> refundDetailVos = new ArrayList<>();
        QueryWrapper<RefundDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("refund_id", refundId);
        Map<Long, OrderDetailVo> hashOrderDetailVo = orderService.getOrderDetail(orderId).stream().collect(Collectors.toMap(OrderDetailVo::getOrderDetailId, odv -> odv));

        for (RefundDetail refundDetail : refundDetailMapper.selectList(queryWrapper)) {
            refundDetailVos.add(new RefundDetailVo(refundDetail, hashOrderDetailVo.get(refundDetail.getOrderDetailId())));
        }
        return refundDetailVos;
    }

    @Override
    public List<Refund> getByUser(Integer status) {
        QueryWrapper<Refund> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", SecurityUtils.getUserIdNumber());
        queryWrapper.orderByDesc("create_time");
        if (status != null) {
            queryWrapper.eq("status", status);
        }
        return baseMapper.selectList(queryWrapper);
    }

    @Transactional
    @Override
    public void submitRefund(RefundVo refundVo) {
        //判断退款订单是否已经退款 或 该订单是否正在退款
        Refund refund = refundVo.getRefund();
        Order order = orderMapper.selectById(refund.getOrderId());
        if (order.getStatus() == 1) {
            throw new BaseException(4000, "该订单还未支付成功");
        }
        //用户是否退款超过三次 用户是否有正在退款的申请
        judgeRefundAble(refund.getOrderId(),0);
        //检测退款金额是否合理，并且返回退款总金额
        refund.setUserId(SecurityUtils.getUserIdNumber());
        refund.setRequireMoneyAmount(checkRefundMoney(refundVo.getRefundDetailList(), 2));
        refund.setStatus(1);
        refund.setOrderNumber(order.getOrderNumber());
        refund.setCreateTime(LocalDateTime.now());
        refund.setRefundNumber("R-" + UUID.randomUUID().toString().substring(0, 5).toUpperCase() + order.getOrderNumber());
        baseMapper.insert(refund);
        refundVo.getRefundDetailList().forEach(rd -> {
            rd.setRefundId(refund.getId());
            rd.setStatus(1);
            refundDetailMapper.insert(rd);
        });
        new LambdaUpdateChainWrapper<>(orderMapper)
                .eq(Order::getId,order.getId())
                .set(Order::getStatus,3).update();
        for(RefundDetail refundDetail : refundVo.getRefundDetailList()){
            new LambdaUpdateChainWrapper<>(orderDetailMapper)
                    .eq(OrderDetail::getId,refundDetail.getOrderDetailId())
                    .set(OrderDetail::getRefundStatus,2).update();
        }
    }

    @Override
    @Transactional
    public void checkRefund(RefundVo refundVo) {
        Refund refund = baseMapper.selectById(refundVo.getRefund().getId());
        if(refund.getStatus() != 1){
            throw new BaseException(4000,"请勿重复操作相同的退款信息");
        }
        if (refundVo.getRefund().getStatus() == 3) {
            Order order = orderMapper.selectById(refund.getOrderId());
            if (order.getStatus() == 1) {
                throw new BaseException(4000, "该订单还未支付");
            }
            refund.setStatus(refundVo.getRefund().getStatus());
            judgeRefundAble(refund.getOrderId(),1);
            judgeRefundIntegrity(refundVo.getRefundDetailList(), refund.getId());
            refund.setActualMoneyAmount(checkRefundMoney(refundVo.getRefundDetailList(), 2));
            refundVo.getRefund().setActualMoneyAmount(refund.getActualMoneyAmount());
            //若总退款金额为0，那么就为全额退款
            refundVo.getRefund().setStatus(3);
//            JSONObject resp = wechatService.orderRefund(refund, order);
//            if ("PROCESSING".equals(resp.getString("status")) || "SUCCESS".equals(resp.getString("status"))) {
//                refundVo.getRefund().setStatus(4);
//            } else {
//                throw new BaseException(4000, "退款失败");
//            }

        }
        Order refundOriginOrder = orderMapper.selectById(refundVo.getRefund().getOrderId());
        refundOriginOrder.setStatus(4);
        orderMapper.updateById(refundOriginOrder);
        //更新操作
        Refund updateRefund = new Refund();
        updateRefund.setId(refundVo.getRefund().getId());
        updateRefund.setActualMoneyAmount(refundVo.getRefund().getActualMoneyAmount());
        updateRefund.setStatus(3);//退款成功
        baseMapper.updateById(updateRefund);
        refundVo.getRefundDetailList().forEach(rd -> {
            RefundDetail updateRefundDetail = new RefundDetail();
            updateRefundDetail.setId(rd.getId());
            updateRefundDetail.setReplyMessage(rd.getReplyMessage());
            updateRefundDetail.setActualMoneyAmount(rd.getActualMoneyAmount());
            updateRefundDetail.setStatus(rd.getStatus());
            refundDetailMapper.updateById(updateRefundDetail);
        });
    }

    public int checkRefundMoney(List<RefundDetail> refundDetailList, Integer status) {
        int refundSum = 0;
        List<Long> orderDetailQuery = refundDetailList.stream().map(RefundDetail::getOrderDetailId).collect(Collectors.toList());
        //获取原来订单的价格
        Map<Long, OrderDetail> hashOrderDetail = new LambdaQueryChainWrapper<>(orderDetailMapper)
                .in(OrderDetail::getId, orderDetailQuery)
                .list().stream().collect(Collectors.toMap(OrderDetail::getId, od -> od));
        //获取已退款订单的价格 可以用status控制，注意status不能为空
        Map<Long, List<RefundDetail>> hashRefundDetail = new LambdaQueryChainWrapper<>(refundDetailMapper)
                .in(RefundDetail::getOrderDetailId, orderDetailQuery)
                .eq(RefundDetail::getStatus, status)
                .list().stream().collect(Collectors.groupingBy(RefundDetail::getOrderDetailId));
        for (RefundDetail refundDetail : refundDetailList) {
            if(refundDetail.getStatus() == 3){
                continue;
            }
            OrderDetail orderDetail = hashOrderDetail.get(refundDetail.getOrderDetailId());
            Integer OriginalMoney = orderDetail.getMoneyAmount();
            Integer refundedAmount = hashRefundDetail.getOrDefault(refundDetail.getOrderDetailId(),new ArrayList<>()).stream().mapToInt(rd -> rd.getActualMoneyAmount() == null ? rd.getRequireMoneyAmount() : rd.getActualMoneyAmount()).sum();
            int nowRefund = refundDetail.getActualMoneyAmount() == null ? refundDetail.getRequireMoneyAmount() : refundDetail.getActualMoneyAmount();
            //计算出退款后还剩多少钱
            int canRefund = OriginalMoney - refundedAmount;
            int restRefund = canRefund - nowRefund;
            if (restRefund < 0) {
                throw new BaseException(4000, "订单中退款金额为" + nowRefund + "的退款申请过多，它还可以退款" + (double) canRefund / 100.0 + "元");
            }
            refundSum += nowRefund;
        }
        return refundSum;
    }

    public void judgeRefundIntegrity(List<RefundDetail> newRefundDetail, Long refundId) {
        Map<Long, List<RefundDetail>> hashNewDetail = newRefundDetail.stream().collect(Collectors.groupingBy(RefundDetail::getId));
        QueryWrapper<RefundDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("refund_id", refundId);
        List<RefundDetail> sqlRefundDetail = refundDetailMapper.selectList(queryWrapper);
        if (hashNewDetail.size() != sqlRefundDetail.size()) {
            throw new BaseException(4000, "您提交的子退款信息有所缺失，请确认没有遗漏的子退款信息");
        }
        for (RefundDetail sqlDetail : sqlRefundDetail) {
            List<RefundDetail> refundDetails = hashNewDetail.getOrDefault(sqlDetail.getId(), null);
            if (refundDetails == null || refundDetails.size() > 1) {
                throw new BaseException(4000, "您重复提交了某个子退款信息");
            }

        }
    }

    public void judgeRefundAble(Long orderId,int num) {
        QueryWrapper<Refund> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_id", orderId);
        queryWrapper.eq("status",1);
        if (baseMapper.selectCount(queryWrapper) > num) {
            throw new BaseException(4000, "该订单已有正在申请的退款，请不要重复申请退款");
        }
    }

    @Transactional
    @Override
    public boolean removeById(Serializable refundId) {
        Refund refund = baseMapper.selectById(refundId);
        if (refund.getStatus() != 1) {
            throw new BaseException(4000, "管理员已审核该退款，无法取消退款");
        }
        QueryWrapper<RefundDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("refund_id", refundId);
        refundDetailMapper.delete(queryWrapper);
        return super.removeById(refundId);
    }

    @Transactional
    @Override
    public void callBack(HttpServletRequest request) {
        try {
            Map<String, String> map = wechatService.refundCallback(request);
            //该退款的数据库信息
            QueryWrapper<Refund> refundQueryWrapper = new QueryWrapper<>();
            refundQueryWrapper.eq("refund_number", map.get("refundNumber"));
            Refund refund = baseMapper.selectOne(refundQueryWrapper);
            QueryWrapper<RefundDetail> refundDetailQuery = new QueryWrapper<>();
            refundDetailQuery.eq("refund_id", refund.getId());
            List<RefundDetail> refundDetailList = refundDetailMapper.selectList(refundDetailQuery);
            //该退款对应的订单信息
            Order order = orderMapper.selectById(refund.getOrderId());
            //需要更新的退款信息
            Refund updateRefund = new Refund();
            updateRefund.setId(refund.getId());
            updateRefund.setSuccessTime(LocalDateTime.now());
            int updateRefundDetailStatus = 2;
            if ("SUCCESS".equals(map.get("status"))) {
                updateRefund.setStatus(3);
            } else {
                //order的status改变
                updateRefund.setStatus(5);
                updateRefundDetailStatus = 4;
            }
            //找到orderDetail对应得所有成功退款信息
            QueryWrapper<Refund> hasRefundQuery = new QueryWrapper<>();
            hasRefundQuery.eq("order_id", order.getId());
            hasRefundQuery.eq("status", 3);
            //成功退款的订单 的 金额
            List<Refund> refundList = baseMapper.selectList(hasRefundQuery);
            int hasRefundMoney = refundList.stream().mapToInt(Refund::getActualMoneyAmount).sum();
            int lastMoney = order.getMoneyAmount() - hasRefundMoney - refund.getActualMoneyAmount();

            if (lastMoney < 0) {
                //退款金额有误会,但微信能成功退款，只是和数据库不匹配
                log.warn("refundNumber = " + refund.getRefundNumber() + "refund.money = " + refund.getActualMoneyAmount() + "wechat.money = " + map.get("payerRefund"));
            }
            if(order.getStatus() != 4){
                new LambdaUpdateChainWrapper<>(orderMapper)
                        .eq(Order::getId, order.getId())
                        .set(Order::getStatus, 4).update();
            }
            //退款信息更新
            baseMapper.updateById(updateRefund);
            for (RefundDetail refundDetail : refundDetailList) {
                if(refundDetail.getStatus() == 3){
                    continue;
                }
                new LambdaUpdateChainWrapper<>(refundDetailMapper)
                        .eq(RefundDetail::getId, refundDetail.getId())
                        .set(RefundDetail::getStatus, updateRefundDetailStatus)
                        .update();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

